#pragma once 
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <fstream>
#include "util.hpp"
#include <mutex>
#include "log.hpp"
namespace ns_index
{
  struct DocInfo
  {
    std::string title;
    std::string content;
    std::string url;
    uint64_t doc_id;           //文档id
  };
  struct InvertedElem
  {
    uint64_t doc_id;
    std::string word;
    int weight;
  };
  typedef std::vector<InvertedElem> InvertedList; //倒排拉链
  class Index
  {
    private:
      Index(){}
      Index(const Index&) = delete;
      Index& operator=(const Index&) = delete;
      static Index* instance;
      static std::mutex mtx;
    public:
      ~Index(){}
      static Index* GetInstance()
      {
        if(nullptr == instance)
        {
          mtx.lock();
          if(nullptr == instance)
          {
            instance = new Index();
          }
          mtx.unlock();
        }
        return instance;
      }
      DocInfo* GetForwardIndex(const uint64_t doc_id)
      {
        if(doc_id >=forward_index.size())
        {
          LOG(WARNING,"doc_id out of range!");
          return nullptr;
        }
        return &forward_index[doc_id];
      }
      InvertedList* GetInvertedList(const std::string& word)
      {
        auto iter = inverted_index.find(word);
        if(iter == inverted_index.end())
        {
          LOG(WARNING, word + "is not in inverted_index!");
          return nullptr;
        }
        return &iter->second;
      }
      //根据去标签格式化后的文档，构建正排和倒排索引
      bool BulidIndex(const std::string& input)
      {
        std::ifstream in(input,std::ios::in | std::ios::binary);
        if(!in.is_open())
        {
          LOG(WARNING,input + "open error!");
          return false;
        }
        std::string line;
        int count = 0;
        while(std::getline(in,line))
        {
          DocInfo* doc =  BuildForwardIndex(line);
          if(nullptr == doc)
          {
            LOG(WARNING,"bulid " + line +" fail!");
            continue;
            //https:www.boost.org/doc/libs/1_79_0/doc/html/BOOST_DLL_ALIAS_SECTIONED.html
          }
          BuildInvertedIndex(*doc);
          count++;
          // 显示一下加载进度
          if(count % 100 == 0)
          {
            LOG(NORMAL, "current build file number:"+ std::to_string(count));
          }
        }
        in.close();
        return true;
      }
    private:
      DocInfo* BuildForwardIndex(const std::string& line)
      {
        //1. 解析line，字符串切分
        std::string sep = "\3";
        std::vector<std::string> results;
        ns_util::StringUtil::Split(line,&results,sep);
        if(results.size() != 3)
        {
          return nullptr;
        }
        //2. 字符串填充到DocInfo
        DocInfo doc;
        doc.title = results[0];
        doc.content = results[1];
        doc.url = results[2];
        doc.doc_id = forward_index.size();
        //3. 插入正排索引的vector
        forward_index.push_back(std::move(doc));
        return &forward_index.back();
      }
      bool BuildInvertedIndex(const DocInfo& doc)
      {
        //对一个doc进行分词并词频检测
        struct word_cnt
        {
          int title_cnt;
          int content_cnt;
          word_cnt():title_cnt(0),content_cnt(0){}
        };
        std::unordered_map<std::string,word_cnt> word_maps; //暂存词频
        std::vector<std::string> title_words;
        ns_util::JiebaUtil::CutString(doc.title,&title_words);
        for(std::string s : title_words)
        {
          boost::to_lower(s);
          word_maps[s].title_cnt++;
        }
        std::vector<std::string> content_words;
        ns_util::JiebaUtil::CutString(doc.content,&content_words);
        for(std::string s:content_words)
        {
          boost::to_lower(s);
          word_maps[s].content_cnt++;
        }
        //建立倒排索引
#define X 10
#define Y 1
        for(auto& word_pair : word_maps)
        {
          InvertedElem item;
          item.doc_id = doc.doc_id;
          item.word = word_pair.first;
          item.weight = X * word_pair.second.title_cnt + Y * word_pair.second.content_cnt;//相关性
          InvertedList& inverted_list = inverted_index[word_pair.first];
          inverted_list.push_back(std::move(item));
        }
        return true;
      }

    private:
      std::vector<DocInfo> forward_index;  //正排索引
      std::unordered_map<std::string, InvertedList> inverted_index; //倒排索引
  };
  Index* Index::instance= nullptr;
  std::mutex Index::mtx;
}
